package com.github.freewu.commons.http;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;

import com.github.freewu.commons.http.string.StringConverterFactory;
import com.github.freewu.commons.utils.MD5;
import com.github.freewu.commons.utils.NetUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;


/**
 * http工具类
 */
public class HttpUtils {
    private static Context context;

    private static String baseUrl = "";

    private static OkHttpClient client;

    private static CacheControl cacheControl;

    private static HttpUtils httpUtils;

    private static Retrofit retrofit;

    public static void init(String baseUrl, Context context) {
        HttpUtils.baseUrl = baseUrl;
        HttpUtils.context = context;
    }

    public synchronized static HttpUtils getInstance() {
        if (client == null) {

            //设置缓存
            Cache cache = new Cache(context.getCacheDir(), 1024 * 1024 * 50);

            //设置超时时间
            client = new OkHttpClient.Builder()
                    .addNetworkInterceptor(REWRITE_RESPONSE_INTERCEPTOR)
                    .addInterceptor(REWRITE_RESPONSE_INTERCEPTOR_OFFLINE)
                    .cache(cache)
                    .connectTimeout(10,TimeUnit.SECONDS)
                    .build();
        }
        if (httpUtils == null) {
            httpUtils = new HttpUtils();

            /**
             * 缓存设置
             */
            cacheControl = new CacheControl.Builder()
                    .maxAge(30, TimeUnit.DAYS)
                    .build();
        }
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(client)
//                    .addConverterFactory(StringConverterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return httpUtils;
    }

    public Retrofit getRetrofit() {
        return retrofit;
    }

    public OkHttpClient getClient() {
        return client;
    }

    public void post(String tag, String url, RequestBody requestBody, final HttpCallBack httpCallBack, HttpFeature... features) {
        int out_time = 60;

        for (HttpFeature feature : features) {
            if (feature instanceof HttpCacheFeature) {
                HttpCacheFeature cacheFeature = (HttpCacheFeature) feature;
            }
            if (feature instanceof HttpTimeOutFeature) {
                HttpTimeOutFeature timeOutFeature = (HttpTimeOutFeature) feature;
                out_time = timeOutFeature.getTime_out();
            }
        }
        post(tag,url, requestBody, httpCallBack, out_time);
    }

    /**
     * @param url          请求链接
     * @param requestBody  请求体
     * @param httpCallBack 请求回调
     * @param out_time     超时时间
     */
    private void post(String tag,String url, RequestBody requestBody, final HttpCallBack httpCallBack, int out_time) {
        final Handler handler = new Handler(Looper.getMainLooper());

        Request.Builder builder = new Request.Builder()
                .url(url)
                .post(requestBody)
                .tag(tag)
                .cacheControl(cacheControl);

        Callback callback = new Callback() {

            @Override
            public void onFailure(final Call call, final IOException e) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        httpCallBack.onError(call, e,HttpURLConnection.HTTP_INTERNAL_ERROR);
                    }
                });
            }

            @Override
            public void onResponse(final Call call, final Response response) throws IOException {
                final String body = response.body().string();
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (response.isSuccessful()) {
                            httpCallBack.onSuccess(call, body);
                        } else {
                            httpCallBack.onError(call, new IOException("服务器数据读取错误"),response.code());
                        }
                        response.close();
                    }
                });
            }
        };

        if (out_time != -1) {
            OkHttpClient new_client = client.newBuilder().connectTimeout(out_time, TimeUnit.SECONDS).build();

            new_client.newCall(builder.build()).enqueue(callback);
        } else {
            client.newCall(builder.build()).enqueue(callback);
        }
    }

    /**
     * @param url         请求连接
     * @param requestBody 请求体
     * @param httpCallBack 回调
     * @return
     */
    public void post(String url, RequestBody requestBody, final HttpCallBack httpCallBack) {
        post("",url, requestBody, httpCallBack, -1);
    }
    /**
     * @param url         请求连接
     * @param requestBody 请求体
     * @param httpCallBack 回调
     * @return
     */
    public void post(String tag,String url, RequestBody requestBody, final HttpCallBack httpCallBack) {
        post(tag,url, requestBody, httpCallBack, -1);
    }

    public void get(String url, final HttpCallBack httpCallBack) {
        get("",url, httpCallBack, -1);
    }
    public void get(String tag,String url, final HttpCallBack httpCallBack) {
        get(tag,url, httpCallBack, -1);
    }
    public void get(String tag,String url,final HttpCallBack httpCallBack, HttpFeature... features) {
        int cache_time = -1;
        int out_time = 60;

        for (HttpFeature feature : features) {
            if (feature instanceof HttpCacheFeature) {
                HttpCacheFeature cacheFeature = (HttpCacheFeature) feature;
                cache_time = cacheFeature.getCache_time();
            }
            if (feature instanceof HttpTimeOutFeature) {
                HttpTimeOutFeature timeOutFeature = (HttpTimeOutFeature) feature;
                out_time = timeOutFeature.getTime_out();
            }
        }
        get(tag,url, httpCallBack, out_time);
    }

    /**
     *
     * @param url
     * @param httpCallBack
     * @param out_time     超时时间
     */
    private void get(String tag,String url, final HttpCallBack httpCallBack, int out_time) {
        final Handler handler = new Handler(Looper.getMainLooper());

        Request.Builder builder = new Request.Builder()
                .url(url)
                .get()
                .tag(tag)
                .cacheControl(cacheControl);

        Callback callback = new Callback() {

            @Override
            public void onFailure(final Call call, final IOException e) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        httpCallBack.onError(call, e, HttpURLConnection.HTTP_INTERNAL_ERROR);
                    }
                });
            }

            @Override
            public void onResponse(final Call call, final Response response) throws IOException {
                final String body = response.body().string();
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (response.isSuccessful()) {
                            httpCallBack.onSuccess(call, body);
                        } else {
                            httpCallBack.onError(call, new IOException("服务器数据读取错误"),response.code());
                        }
                        response.close();
                    }
                });
            }
        };

        if (out_time != -1) {
            OkHttpClient new_client = client.newBuilder().connectTimeout(out_time, TimeUnit.SECONDS).build();

            new_client.newCall(builder.build()).enqueue(callback);
        } else {
            client.newCall(builder.build()).enqueue(callback);
        }
    }

    /**
     * 下载文件
     *
     * @param url          下载链接
     * @param path         下载的文件路径
     * @param fileCallBack 回调
     */
    public void downloadFile(String url, final File path, final HttpFileCallBack fileCallBack) {
        final Handler handler = new Handler(Looper.getMainLooper());

        final Request request = new Request.Builder()
                .url(url)
                .get()
                .cacheControl(cacheControl)
                .build();
        client.newCall(request).enqueue(new Callback() {

            @Override
            public void onFailure(final Call call, final IOException e) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        fileCallBack.onError(call, e);
                    }
                });
            }

            @Override
            public void onResponse(final Call call, final Response response) throws IOException {
                InputStream is = response.body().byteStream();
                FileOutputStream fos;
                try {
                    fos = new FileOutputStream(path);
                }catch (final Exception e) {
                    e.printStackTrace();

                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            fileCallBack.onError(call, e);
                        }
                    });

                    is.close();

                    return;
                }
                final long length = response.body().contentLength();
                long getleng = 0;

                byte[] bytes = new byte[1024];

                int index = -1;

                do {
                    index = is.read(bytes);
                    if (index != -1) {
                        fos.write(bytes, 0, index);
                        getleng = getleng + index;

                        final long finalGetleng = getleng;
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                fileCallBack.onProgress(finalGetleng, length);
                            }
                        });
                    }
                } while (index != -1);

                is.close();

                fos.flush();
                fos.close();

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        fileCallBack.onSuccess(call, path);
                        response.close();
                    }
                });
            }
        });
    }

    /**
     * 通过tag来结束掉链接
     * @param tag 标致
     */
    public void cancelConnect(String tag){
        if (client!=null){
            for(Call call:client.dispatcher().runningCalls()){
                if (call.request().tag().equals(tag)) {
                    call.cancel();
                }
            }
            for(Call call:client.dispatcher().queuedCalls()){
                if (call.request().tag().equals(tag)) {
                    call.cancel();
                }
            }
        }
    }

    /**
     * 结束所有链接
     */
    public void cancelAll(){
        if (client!=null){
            client.dispatcher().cancelAll();
        }
    }

    private static final Interceptor REWRITE_RESPONSE_INTERCEPTOR = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            okhttp3.Response originalResponse = chain.proceed(chain.request());
            String cacheControl = originalResponse.header("Cache-Control");
            if (cacheControl == null) {
                //如果cache没值，缓存时间为TIMEOUT_CONNECT，有的话就为cache的值
                originalResponse = originalResponse.newBuilder()
                        .header("Cache-Control", "public, max-age=0")
                        .build();

                return originalResponse;
            } else {
                return originalResponse;
            }
        }
    };

    private static final Interceptor REWRITE_RESPONSE_INTERCEPTOR_OFFLINE = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            try {
                Response response = chain.proceed(request);

                return response;
            } catch (UnknownHostException | SocketTimeoutException e) {
                request = request.newBuilder()
                        .cacheControl(CacheControl.FORCE_CACHE)
                        .build();

                return chain.proceed(request);
            }
        }
    };

    /**
     * 设置证书文件
     *
     * @param certificates
     */
    public void setCertificates(InputStream... certificates) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));

                try {
                    if (certificate != null)
                        certificate.close();
                } catch (IOException e) {

                }
            }

            SSLContext sslContext = SSLContext.getInstance("TLS");

            TrustManagerFactory trustManagerFactory =
                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

            trustManagerFactory.init(keyStore);
            sslContext.init
                    (
                            null,
                            trustManagerFactory.getTrustManagers(),
                            new SecureRandom()
                    );
//            mOkHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
